using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Xml.XPath;

namespace PRXVerifier
{
    public enum VerifierResult
    {
        PRXMatch,
        XPathMatch,
        NoMatch
    }

    public class PRXVerfier
    {
        private List<XmlDocument> baselinePRXs = new List<XmlDocument>();

        public List<XmlDocument> BaselinePRXs
        {
            get { return baselinePRXs; }
            set { baselinePRXs = value; }
        }

        private List<string> filterXpaths = new List<string>();
        
        public List<string> FilterXPaths
        {
            get { return filterXpaths; }
            set { filterXpaths = value; }
        }

        private List<string> matchXPaths = new List<string>();

        public List<string> MatchXPaths
        {
            get { return matchXPaths; }
            set { matchXPaths = value; }
        }

        private string messages = string.Empty;

        public string Messages
        {
            get { return messages; }
            set { messages = value; }
        }
	
        public VerifierResult Verify(string wmvFile)
        {
            if(!File.Exists(wmvFile))
                return VerifierResult.NoMatch;

            VerifierResult retVal = VerifierResult.NoMatch;

            XmlDocument wmvProfile = GetProfileDocumentFromWMV(wmvFile);

            XPathNavigator nav = wmvProfile.CreateNavigator();

            foreach(string node in FilterXPaths)
            {
                XPathNavigator filteredNode = nav.SelectSingleNode(node);
                if (filteredNode != null)
                {
                    nav.MoveTo(filteredNode);
                    nav.DeleteSelf();
                }
            }

            //CompareViaText(wmvProfile);
            foreach(XmlDocument baselinePRX in BaselinePRXs)
            {
                messages = string.Empty;
                if (CompareNode(baselinePRX.DocumentElement, wmvProfile.DocumentElement))
                {
                    retVal = VerifierResult.PRXMatch;
                    break;
                }
                else
                {
                    foreach (string xpath in MatchXPaths)
                    {
                        XmlNode expectedNode = baselinePRX.SelectSingleNode(xpath);
                        XmlNode actualNode = wmvProfile.SelectSingleNode(xpath);
                        if (expectedNode != null && actualNode != null)
                        {
                            if (CompareNode(expectedNode, actualNode, false, false))
                                retVal = VerifierResult.XPathMatch;
                            else
                            {
                                messages += String.Format("First node with differences{0}{0}Expected:{1}{0}Actual:{2}{0}{0}", System.Environment.NewLine, expectedNode.OuterXml,actualNode.OuterXml);
                                retVal = VerifierResult.NoMatch;
                                break;
                            }
                        }
                        else
                        {
                            retVal = VerifierResult.NoMatch;
                            break;
                        }
                    }
                }
            }
            

            return retVal;
        }

        private XmlDocument GetProfileDocumentFromWMV(string wmvFile)
        {
            IWMSyncReader reader;
            NativeMethods.WMCreateSyncReader(null, 1U, out reader);

            reader.Open(wmvFile);
            IWMProfile profile = reader as IWMProfile;
            if (profile != null)
            {
                IWMProfileManager profileManager;
                uint fileSize = 0;
                NativeMethods.WMCreateProfileManager(out profileManager);
                uint saveFirstHRESULT = profileManager.SaveProfile(profile, null, ref fileSize);
                StringBuilder profileText = new StringBuilder((int)fileSize);
                uint saveSecondHRESULT = profileManager.SaveProfile(profile, profileText, ref fileSize);
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(profileText.ToString());
                return doc;
            }
            return null;
        }

        public void SavePRXToDisk(string wmvFile, string prxFile)
        {
            XmlDocument profileDocument = GetProfileDocumentFromWMV(wmvFile);
            if(profileDocument != null)
                profileDocument.Save(prxFile);
        }

        private bool CompareNode(XmlNode expectedNode, XmlNode actualNode)
        {
            return CompareNode(expectedNode, actualNode, true, true);
        }

        private bool CompareNode(XmlNode expectedNode, XmlNode actualNode, bool checkChildren, bool checkAttributes)
        {
            if (expectedNode.Name != actualNode.Name)
                return false;
            if (expectedNode.Value != actualNode.Value)
                return false;
            if (checkChildren)
            {
                if ((expectedNode.ChildNodes == null && actualNode.ChildNodes != null) || 
                    (expectedNode.ChildNodes != null && actualNode.ChildNodes == null))
                    return false;
                if (!(expectedNode.ChildNodes == null))
                {
                    if (expectedNode.ChildNodes.Count != actualNode.ChildNodes.Count)
                        return false;
                    for (int x = 0; x < expectedNode.ChildNodes.Count; x++)
                    {
                        if (!CompareNode(expectedNode.ChildNodes[x], actualNode.ChildNodes[x]))
                            return false;
                    }
                }
            }
            if (checkAttributes)
            {
                if ((expectedNode.Attributes == null && actualNode.Attributes != null) ||
                    (expectedNode.Attributes != null && actualNode.Attributes == null))
                    return false;
                if (!(expectedNode.Attributes == null))
                {
                    if (expectedNode.Attributes.Count != actualNode.Attributes.Count)
                        return false;
                    for (int x = 0; x < expectedNode.Attributes.Count; x++)
                    {
                        if (!CompareNode(expectedNode.Attributes[x], actualNode.Attributes[x]))
                            return false;
                    }
                }
            }
            return true;
        }
    }
}
